#pragma once

#include <iostream>
#include "Singleton.h"

class Log : public Singleton<Log>
{
public:
	enum LogLevel
	{
		Notice,
		Warning,
		Error,
		Debug,
	};
private:

	void setColor(LogLevel level) {
		auto handle = GetStdHandle(STD_OUTPUT_HANDLE);
		switch(level) {
		case Notice:
			SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY);
			break;

		case Warning:
			SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
			break;

		case Error:
			SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_INTENSITY);
			break;

		case Debug:
			SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
			break;

		default:
			SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
			break;
		}
	}

	std::vector<std::ostream*> mLogs;
public:
	Log() {
		mLogs.push_back(&std::cout);
		mLogs.push_back(&std::cout);
		mLogs.push_back(&std::cout);
		mLogs.push_back(&std::cout);
	}

	std::ostream& getLog(LogLevel level) {
		if((uint32)level > mLogs.size())
			throw std::out_of_range("Log level is not valid!");

		return *mLogs[(uint32)level];
	}

	void setLog(std::ostream& log, LogLevel level) {
		if((uint32)level > mLogs.size())
			return;

		mLogs[(uint32)level] = &log;
	}

	std::ostream& debug() {
		std::ostream& strm = *mLogs[Debug];
		setColor(Debug);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " Debug: ";
		setColor((LogLevel)-1);
		return strm;
	}

	std::ostream& notice(const std::string& title = "Notice") {
		std::ostream& strm = *mLogs[Notice];
		setColor(Notice);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " N " << title << ": ";
		setColor((LogLevel)-1);
		return strm;
	}

	std::ostream& warning() {
		std::ostream& strm = *mLogs[Warning];
		setColor(Warning);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " Warning: ";
		setColor((LogLevel)-1);
		return strm;
	}

	std::ostream& error() {
		std::ostream& strm = *mLogs[Error];
		setColor(Error);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " Error: ";
		setColor((LogLevel)-1);
		return strm;
	}

	void outDebug(const std::string& title, const std::string& message) {
		std::ostream& strm =*mLogs[Debug];
		setColor(Debug);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " D " << title << ": ";
		setColor((LogLevel)-1);
		strm << message << std::endl;
	}

	void outNotice(const std::string& title, const std::string& message) {
		std::ostream& strm = *mLogs[Notice];
		setColor(Notice);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " N " << title << ": ";
		setColor((LogLevel)-1);
		strm << message << std::endl;
	}

	void outWarning(const std::string& title, const std::string& message) {
		std::ostream& strm = *mLogs[Warning];
		setColor(Warning);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " W " << title << ": ";
		setColor((LogLevel)-1);
		strm << message << std::endl;
	}

	void outError(const std::string& title, const std::string& message) {
		std::ostream& strm = *mLogs[Error];
		setColor(Error);
		time_t tM;
		time(&tM);
		tm lt;
		localtime_s(&lt, &tM);

		strm << std::setw(2) << std::setfill('0') << lt.tm_hour << std::setw(1) << ':' << std::setw(2) << lt.tm_min << std::setw(1) << ':' << std::setw(2) << lt.tm_sec;
		strm << " E " << title << ": ";
		setColor((LogLevel)-1);
		strm << message << std::endl;
	}
};

#define sLog (Log::getInstance())